﻿// (c) Copyright Clovis Coli Jr.
// This source is subject to the Microsoft Public License.
// See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Forms;
using System.Windows.Media;
using System.Reflection;
using System.Windows.Shapes;
using Control = System.Windows.Controls.Control;
using HorizontalAlignment = System.Windows.HorizontalAlignment;
using Panel = System.Windows.Controls.Panel;

namespace BrasilFramework.Controle.WPF.Comum.Validacao
{
    public enum ValidationType
    {
        Validator,
        OnDemand
    }

    public abstract class ValidatorBase : DependencyObject
    {
        protected ValidatorManager Manager { get; set; }

        public bool IsShowIndicator { get; set; }
        public string DefaultValue { get; set; }
        public string PropertyToValidate { get; set; }
        public string ManagerName { get; set; }
        public ValidationType ValidationType { get; set; }
        public FrameworkElement ElementToValidate { get; set; }
        public bool IsRequired { get; set; }
        public bool IsValid { get; set; }
        public Brush InvalidBackground { get; set; }
        public Brush InvalidBorder { get; set; }
        public Thickness InvalidBorderThickness { get; set; }
        public string ErrorMessage { get; set; }

        private Brush _origBackground;
        private Brush _origBorder;
        private Thickness _origBorderThickness = new Thickness(1);
        private object _origTooltip;

        protected ValidatorBase()
        {
            IsValid = true;
            ManagerName = "";
            ValidationType = ValidationType.Validator;
        }

        public void Initialize(FrameworkElement element)
        {
            ElementToValidate = element;
            element.Loaded += ElementLoaded;
        }

        private bool _loaded;
        public FrameworkElement FrameworkElement { get; set; }
        private void ElementLoaded(object sender, RoutedEventArgs e)
        {
            if (!_loaded)
            {
                Inicializar();
                _loaded = true;
            }
            ElementToValidate.Loaded -= ElementLoaded;
        }

        public void Inicializar()
        {

            //Caso o gerenciador não esteja configurado o adiciona na raiz (LayoutRoot)
            if (Manager == null)
            {
                FrameworkElement = FindRootFrameworkElement(ElementToValidate);
                if (FrameworkElement == null) return;
            }

            Manager = FindManager(FrameworkElement);
            if (Manager == null)
                throw new Exception(String.Format("No ValidatorManager found named '{0}'", ManagerName));
            
            Manager.Register(ElementToValidate, this);

            if (ValidationType == ValidationType.Validator) ActivateValidationRoutine();

            if (InvalidBackground == null) InvalidBackground = Manager.InvalidBackground;

            if (InvalidBorder != null) return;

            InvalidBorder = Manager.InvalidBorder;

            if (InvalidBorderThickness.Bottom == 0)
            {
                InvalidBorderThickness = Manager.InvalidBorderThickness;
            }
        }

        public void Finalizar()
        {
            Manager.Unregister(ElementToValidate, this);
            if (ValidationType == ValidationType.Validator) DeactivateValidationRoutine();
        }

        public void SetManagerAndControl(ValidatorManager manager, FrameworkElement element)
        {
            Manager = manager;
            ElementToValidate = element;
        }

        public bool Validate(bool checkControl)
        {
            bool newIsValid;
            if (checkControl)
            {
                newIsValid = ValidateControl() && ValidateRequired();
            }
            else
            {
                newIsValid = ValidateRequired();
            }

            if (newIsValid && !IsValid)
            {
                ControlValid();
            }
            if (!newIsValid && IsValid)
            {
                ControlNotValid();
            }
            IsValid = newIsValid;
            return IsValid;
        }

        public virtual void ActivateValidationRoutine()
        {
            ElementToValidate.LostFocus += ElementToValidateLostFocus;

            if (ElementToValidate is ComboBox) return;
            ElementToValidate.KeyUp += ElementToValidateKeyUp;
        }

        public virtual void DeactivateValidationRoutine()
        {
            ElementToValidate.LostFocus -= ElementToValidateLostFocus;

            if (ElementToValidate is ComboBox) return;
            ElementToValidate.KeyUp -= ElementToValidateKeyUp;
        }

        /// <summary>
        /// Find the nearest FrameworkElement up the control tree for the FrameworkElement passed in
        /// </summary>
        /// <param name="element">Control to validate</param>
        protected static FrameworkElement FindRootFrameworkElement(FrameworkElement element)
        {
            if (element == null)
            {
                return null;
            }
            if (element.Parent != null)
            {
                if (element.Parent is Page || element.Parent is Window)
                {
                    return element.Parent as FrameworkElement;
                }
                return FindRootFrameworkElement(element.Parent as FrameworkElement);
            }
            return null;
        }

        protected virtual void ElementToValidateKeyUp(object sender, System.Windows.Input.KeyEventArgs e)
        {
            Dispatcher.BeginInvoke(new MethodInvoker(() => Validate(false)), null);
        }

        protected virtual void ElementToValidateLostFocus(object sender, RoutedEventArgs e)
        {
            Dispatcher.BeginInvoke(new MethodInvoker(() => Validate(true)), null);
        }

        protected abstract bool ValidateControl();

        protected bool ValidateRequired()
        {
            if (!IsRequired) return true;

            string defaultProperty = !string.IsNullOrEmpty(PropertyToValidate)
                                         ? PropertyToValidate
                                         : ElementToValidate is PasswordBox ? "Password" : "Text";

            var pPropertyValidade = ElementToValidate.GetType().GetProperty(defaultProperty);

            if (pPropertyValidade != null)
            {
                var value = pPropertyValidade.GetValue(ElementToValidate, null);

                if (ElementToValidate is System.Windows.Controls.ComboBox && value is KeyValuePair<object, string>)
                    value = ((KeyValuePair<object, string>)value).Value;

                if (value is String)
                    return !String.IsNullOrEmpty((String)value) && (String)value != DefaultValue;

                if (value is double || value is long || value is int || value is decimal || value is uint || value is float)
                    return !String.IsNullOrEmpty(value.ToString())
                        && value.ToString() !=
                        (string.IsNullOrEmpty(DefaultValue) ? "0" : DefaultValue);

                return value != null;
            }

            return true;
        }

        protected void ControlNotValid()
        {
            GoToInvalidStyle();
        }

        protected void ControlValid()
        {
            GoToValidStyle();
        }

        protected virtual void GoToInvalidStyle()
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                var tooltip = ToolTipService.GetToolTip(ElementToValidate);

                if (tooltip != null)
                {
                    _origTooltip = tooltip;
                }

                ElementToValidate.ClearValue(ToolTipService.ToolTipProperty);

                SetToolTip(ElementToValidate, ErrorMessage);
            }

            if (IsShowIndicator && ElementToValidate != null)
            {
                var control = (Control)ElementToValidate;
                double altura = 16;
                double largura = 16;

                if (control.ActualHeight > 32) altura = largura = 24;

                Grid areaControle = new Grid { Background = control.Background, Width = control.ActualWidth - largura };
                Grid areaCirculo = new Grid { Width = largura, Height = altura, HorizontalAlignment = HorizontalAlignment.Right, VerticalAlignment = VerticalAlignment.Center };
                Ellipse circulo = new Ellipse { Fill = Brushes.Red };
                TextBlock texto = new TextBlock { Text = "!", Foreground = Brushes.White, FontWeight = FontWeights.Bold, HorizontalAlignment = HorizontalAlignment.Center, VerticalAlignment = VerticalAlignment.Center };
                areaCirculo.Children.Add(circulo);
                areaCirculo.Children.Add(texto);
                areaControle.Children.Add(areaCirculo);
                VisualBrush areaDesenho = new VisualBrush(areaControle);
                control.Background = areaDesenho;
            }

            if (ElementToValidate == null) return;

            var pBackground = ElementToValidate.GetType().GetProperty("Background");

            if (pBackground != null)
            {
                if (InvalidBackground != null)
                {
                    if (_origBackground == null)
                    {
                        _origBackground = (Brush)pBackground.GetValue(ElementToValidate, null);
                    }
                    pBackground.SetValue(ElementToValidate, InvalidBackground, null);
                }
            }

            var pBorderBrush = ElementToValidate.GetType().GetProperty("BorderBrush");
            var pBorderThickness = ElementToValidate.GetType().GetProperty("BorderThickness");

            if (pBorderBrush == null || pBorderThickness == null) return;

            if (InvalidBorder == null) return;

            if (_origBorder == null)
            {
                _origBorder = (Brush)pBorderBrush.GetValue(ElementToValidate, null);
                _origBorderThickness = (Thickness)pBorderThickness.GetValue(ElementToValidate, null);
            }
            pBorderBrush.SetValue(ElementToValidate, InvalidBorder, null);

            if (InvalidBorderThickness != null)
            {
                pBorderThickness.SetValue(ElementToValidate, InvalidBorderThickness, null);
            }
        }

        protected virtual void GoToValidStyle()
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                ElementToValidate.ClearValue(ToolTipService.ToolTipProperty);

                if (_origTooltip != null)
                {
                    SetToolTip(ElementToValidate, _origTooltip);
                }
            }

            var control = ElementToValidate as Control;
            if (control != null) control.Background = new SolidColorBrush(Colors.White);

            if (ElementToValidate == null) return;

            PropertyInfo pBackground = ElementToValidate.GetType().GetProperty("Background");

            if (pBackground != null)
            {
                if (_origBackground != null)
                {
                    pBackground.SetValue(ElementToValidate, _origBackground, null);
                }
            }

            PropertyInfo pBorderBrush = ElementToValidate.GetType().GetProperty("BorderBrush");
            PropertyInfo pBorderThickness = ElementToValidate.GetType().GetProperty("BorderThickness");

            if (pBorderBrush == null || pBorderThickness == null) return;

            if (_origBorder == null) return;

            pBorderBrush.SetValue(ElementToValidate, _origBorder, null);

            if (_origBorderThickness != null)
            {
                pBorderThickness.SetValue(ElementToValidate, _origBorderThickness, null);
            }
        }

        protected void SetToolTip(FrameworkElement element, object tooltip)
        {
            Dispatcher.BeginInvoke(new MethodInvoker(() => ToolTipService.SetToolTip(element, tooltip)), null);
        }

        private ValidatorManager FindManager(FrameworkElement p)
        {
            const string defaultName = "_DefaultValidatorManager";
            var mgr = Manager ?? (FrameworkElement.FindName(ManagerName) ?? FrameworkElement.FindName(defaultName));
            if (mgr == null)
            {
                mgr = new ValidatorManager
                          {
                              Name = defaultName
                          };
                var g = p.FindName("LayoutRoot") as Panel;
                if (g != null) g.Children.Add(mgr as ValidatorManager);
            }
            return mgr as ValidatorManager;
        }

    }
}